package com.cqhilink.api.common.utils;

import com.alibaba.fastjson.JSON;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * HTTP工具类
 *
 * @author 王镇
 * @create 2017-02-13 17:46
 */
public class HttpClientUtil {
    private static final String APPLICATION_JSON = "application/json";
    private static final String CONTENT_TYPE_TEXT_JSON = "text/json";
    private static final int socketTimeout = 5000;// 请求超时时间
    private static final int connectTimeout = 5000;// 传输超时时间
    private static final int connectionRequestTimeout = 1000; // 获取Connection 超时时间
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    //==========4.3之后过时替换============//
    // DefaultHttpClient —> CloseableHttpClient
    // HttpResponse —> CloseableCloseableHttpResponse

    /**
     * 发送HTTP POST请求
     *
     * @param url     请求地址
     * @param content 请求内容
     * @return 响应报文
     */
    public static String httpPost(String url, final String content) throws IOException {
        logger.debug("请求地址：" + url);
        logger.debug("请求报文：" + content);
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost method = new HttpPost(url);
        ContentProducer cp = new ContentProducer() {
            @Override
            public void writeTo(OutputStream outstream) throws IOException {
                //2.2.传递app所请求的参数
                Writer writer = new OutputStreamWriter(outstream, HTTP.UTF_8);
                writer.write(content);
                writer.flush();
            }
        };
        method.setEntity(new EntityTemplate(cp));
        CloseableHttpResponse response = client.execute(method);
        String resp = EntityUtils.toString(response.getEntity());
        logger.debug("响应报文：" + resp);
        return resp;
    }

    /**
     * 发送HTTP POST请求(Json)
     *
     * @param url     请求地址
     * @param content 请求内容
     * @return 响应报文
     */
    public static String httpPostWithJson(String url, final String content) throws IOException {
        logger.debug("请求地址：" + url);
        logger.debug("请求报文：" + content);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
        //String encoderJson = URLEncoder.encode(content, HTTP.UTF_8);
        StringEntity se = new StringEntity(content, Charset.forName("UTF-8"));
        se.setContentType(CONTENT_TYPE_TEXT_JSON);
        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON));
        httpPost.setEntity(se);

        CloseableHttpResponse response = httpClient.execute(httpPost);
        String resp = EntityUtils.toString(response.getEntity());
        logger.debug("响应报文：" + resp);
        return resp;
    }

    /**
     * 发送HTTP POST请求(Json)
     *
     * @param url      请求地址
     * @param content  请求内容
     * @param cTimeout 请求连接超时时间
     * @param sTimeout 请求数据返回超时时间
     * @return
     * @throws IOException
     */
    public static String httpPostWithJson(String url, final String content, int cTimeout, int sTimeout) throws IOException {
        logger.debug("请求地址：" + url);
        logger.debug("请求报文：" + content);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 设置超时时间
        // setConnectTimeout：设置连接超时时间，单位毫秒
        // setConnectionRequestTimeout：设置从connect Manager获取Connection 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的
        // setSocketTimeout：请求获取数据的超时时间，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用
        int connectTimeouts = cTimeout == 0 ? connectTimeout : cTimeout;
        int socketTimeouts = sTimeout == 0 ? socketTimeout : sTimeout;
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(connectTimeouts)
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .setSocketTimeout(socketTimeouts).build();

        HttpPost httpPost = new HttpPost(url);

        httpPost.setConfig(requestConfig);
        httpPost.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
        // String encoderJson = URLEncoder.encode(content, Charset.forName("UTF-8");
        // 构建消息实体
        StringEntity se = new StringEntity(content, Charset.forName("UTF-8"));
        // 设置实体的编码格式
        se.setContentEncoding("UTF-8");
        // 发送Json格式的数据请求
        se.setContentType(CONTENT_TYPE_TEXT_JSON);
        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON));
        httpPost.setEntity(se);

        CloseableHttpResponse response = httpClient.execute(httpPost);
        String resp = EntityUtils.toString(response.getEntity());
        logger.debug("响应报文：" + resp);
        return resp;
    }

    /**
     * 发送HTTP GET请求
     *
     * @param url 请求地址
     * @return 响应报文
     */
    public static String httpGet(String url) {
        logger.debug("请求地址：" + url);
        CloseableHttpClient client = HttpClients.createDefault();
        //根据服务管理请求地址，进行http请求
        HttpGet request = new HttpGet();
        //2.3.执行http请求
        CloseableHttpResponse httpResponse;
        String resp = "";
        try {
            request.setURI(new URI(url));
            httpResponse = client.execute(request);
            //3.获取服务管理返回值
            resp = EntityUtils.toString(httpResponse.getEntity(), Charset.forName("UTF-8"));
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            request.releaseConnection();
            //client.getConnectionManager().shutdown(); //关闭连接,释放资源
        }
        logger.debug("响应报文：" + resp);
        return resp;
    }

    /**
     * 发送Method Post请求
     *
     * @param url    请求地址
     * @param params 表单数据
     * @return 响应报文
     */
    public static String methodPost(String url, Map<String, Object> params) {
        logger.debug("请求地址：" + url);
        logger.debug("请求报文：" + JSON.toJSON(params));
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        String resp = "";
        try {
            // 表单数据
            List<NameValuePair> content = new ArrayList<>();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                content.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(content, Charset.forName("UTF-8")));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            System.out.println("<< Response: " + response.getStatusLine());
            if (entity != null) {
                resp = EntityUtils.toString(entity);
            }
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        } catch (ClientProtocolException e) {
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
        logger.debug("响应报文：" + resp);
        return resp;
    }

    /**
     * 发送https请求
     *
     * @param url     请求地址
     * @param message 请求报文
     * @return 响应
     */
    public static String sslPostWithJson(String url, final String message) {
        logger.debug("请求地址：" + url);
        logger.debug("请求报文：" + message);
        String responseContent = null; //响应内容
        CloseableHttpClient httpClient = HttpClients.createDefault(); //创建默认的httpClient实例
        X509TrustManager xtm = new X509TrustManager() { //创建TrustManager
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        try {
            //TLS1.0与SSL3.0基本上没有太大的差别，可粗略理解为TLS是SSL的继承者，但它们使用的是相同的SSLContext
            SSLContext ctx = SSLContext.getInstance("TLS");
            //使用TrustManager来初始化该上下文，TrustManager只是被SSL的Socket所使用
            ctx.init(null, new TrustManager[]{xtm}, null);
            //创建SSLSocketFactory
            SSLSocketFactory socketFactory = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            //通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
            httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
            HttpPost httpPost = new HttpPost(url); //创建HttpPost
            httpPost.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
            StringEntity se = new StringEntity(message, Charset.forName("UTF-8"));
            se.setContentType(CONTENT_TYPE_TEXT_JSON);
            se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON));
            httpPost.setEntity(se);
            CloseableHttpResponse response = httpClient.execute(httpPost);
            responseContent = EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
        logger.debug("响应报文：" + responseContent);

        return responseContent;
    }

    /**
     * rest post get 请求
     *
     * @param urlString
     * @param requestMethod
     * @param content
     * @return
     */
    public static String httpRest(String urlString, String requestMethod, String content) {
        HttpURLConnection connection = null;
        String resp = "";
        try {
            //
            URL url = new URL(urlString);
            // 根据url打开连接
            connection = (HttpURLConnection) url.openConnection();
            // POST：检索或者创建一个资源
            // Get：检索一个资源
            // PUT: 更新一个资源
            // DELETE：删除一个资源
            connection.setRequestMethod(requestMethod);
            // 设置用户名密码
            BASE64Encoder encoder = new BASE64Encoder();
            String encodedCredential = encoder.encode(content.getBytes());
            // 设置请求属性
            connection.setRequestProperty("Authorization", "Basic " + encodedCredential);
            // 获取调用结果
            connection.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            int responseCode = connection.getResponseCode();
            // BPM服务器响应成功
            if (responseCode == HttpURLConnection.HTTP_OK) {
                StringBuffer stringBuffer = new StringBuffer();
                String line = "";
                // 如果能从流中读取内容
                while ((line = reader.readLine()) != null) {
                    stringBuffer.append(line);
                }
                resp = stringBuffer.toString();
                logger.info("resp返回信息：{}", resp);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return resp;
    }

    /**
     * 使用SOAP1.1发送消息
     * <p>
     * 百度：httpclient 处理webservice 请求 提交报文
     * https://www.2cto.com/kf/201701/583039.html
     * 优点：
     * 1.使用httpclient作为客户端调用webservice，不用关注繁琐的webservice框架，只需找到SOAP消息格式，添加httpclient依赖就行。
     * 2.使用httpclient调用webservice，建议采用soap1.1方式调用，经测试使用soap1.1方式能调用soap1.1和soap1.2的服务端。
     * 缺点：唯一的缺点是，你得自己解析返回的XML，找到你关注的信息内容。
     *
     * @param postUrl
     * @param soapXml
     * @param soapAction
     * @return
     */
    public static String doPostSoap1_1(String postUrl, String soapXml, String soapAction) {
        String retStr = "";
        // 创建HttpClientBuilder
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // HttpClient
        CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
        HttpPost httpPost = new HttpPost(postUrl);
        // 设置请求和传输超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        try {
            httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8");
            httpPost.setHeader("SOAPAction", soapAction);
            StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8"));
            httpPost.setEntity(data);
            CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
            HttpEntity httpEntity = response.getEntity();
            if (httpEntity != null) {
                // 打印响应内容
                retStr = EntityUtils.toString(httpEntity, "UTF-8");
                logger.info("response:" + retStr);
            }
            // 释放资源
            closeableHttpClient.close();
        } catch (Exception e) {
            logger.error("exception in doPostSoap1_1", e);
        }
        return retStr;
    }

    /**
     * 使用SOAP1.2发送消息
     *
     * @param postUrl
     * @param soapXml
     * @param soapAction
     * @return
     */
    public static String doPostSoap1_2(String postUrl, String soapXml, String soapAction) {
        String retStr = "";
        // 创建HttpClientBuilder
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // HttpClient
        CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
        HttpPost httpPost = new HttpPost(postUrl);
        // 设置请求和传输超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        try {
            httpPost.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
            httpPost.setHeader("SOAPAction", soapAction);
            StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8"));
            httpPost.setEntity(data);
            CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
            HttpEntity httpEntity = response.getEntity();
            if (httpEntity != null) {
                // 打印响应内容
                retStr = EntityUtils.toString(httpEntity, "UTF-8");
                logger.info("response:" + retStr);
            }
            // 释放资源
            closeableHttpClient.close();
        } catch (Exception e) {
            logger.error("exception in doPostSoap1_2", e);
        }
        return retStr;
    }
}
